昨天我們把熱力圖需要的正方形擺得整整齊齊的,那接下來就是熱力圖最重要的部分:把區塊上色。
在實作之前,我們先來理解一下要如何使用d3做色彩的管理。
顏色絕對是設計不可或缺的一部分,我們在寫css style時也常常使用到顏色,最常使用的是hex及rgba,那麼色碼到底是什麼?
首先是rgb色彩,國中小的自然課都有學到,光的三原色為紅、綠、藍,分別代表的就是r、g、b。
而我們在設定rgb色彩時,都會分別填入0~255的數值rgb(0-255, 0-255, 0-255),這些數值代表的意義很簡單,就是色光的強度,0就是燈沒開,255的燈就是全開的。因此rgb(0, 0, 0)就是三色燈都沒開=黑色;rgb(255, 255, 255)就是三色燈全開=白色。
那rgba中的a又是什麼,a為Alpha(色彩空間),以應用面來看為不透明度參數,0是完全透明、100則是完全不透明。
下一個是hex色碼,也就是我們常看到的#加上六個字母or數字,這幾個字代表的意義很容易,前兩個為紅色、中間兩個為綠色,最後兩個是藍色,意義與rgb完全相同,00是全關,FF為全開,此為十六進位制(0,1,2....a,b,c...f)。
有了色彩的知識以後,我們現在知道那些數字、字母都是有意義的,也就是說可以被量化,而d3就是利用這個原理去管理、調整色彩的。例如brighter、darke的funciton可以讓顏色變亮變暗,也可以應用在scale當中去計算特定的漸層顏色,我們今天就是用這個方式去產生Heat Map中每個數值所轉換出來的色彩。
d3 color的api文件:https://github.com/d3/d3-color
我們現在就來幫昨天做完的方形們塗上顏色
const colorScale = d3.scaleLinear()
.domain([0, d3.max(data.map((e) => e.value)) / 2, d3.max(data.map((e) => e.value))])
// 我這邊的間層色有三個色彩點,分別代表0, 正中間的數值, 最大的數值
.range(['#F3F3F3', '#84B6FD', '#8E87FA']);
g.selectAll('.block')
.data(data)
.enter()
.append('rect')
.attr('class', 'block')
.attr('x', (d) => (d.weekNum - 1) * 15 + (d.weekNum - 1) * 1)
.attr('y', (d) => d.day * 15 + d.day * 1)
// 超簡單,一樣直接value塞進去
.attr('fill', (d) => colorScale(d.value))
.attr('width', 15)
.attr('height', 15)
.attr('rx', 3);
再來標上座標就完成熱力圖囉,感覺比長條圖更簡單~
const days = [0, 1, 2, 3, 4, 5, 6];
g
.selectAll('.day-label')
.data(days)
.enter()
.append('text')
.attr('class', 'day-label')
.text((d) =>
moment()
.isoWeekday(d)
.format('ddd') //星期的前三個字
)
.attr('font-size', 8)
.attr('fill', '#B8B8B8')
.attr('opacity', (d, i) => (i % 2 === 0 ? 1 : 0)) //index是奇數才顯示
.attr('x', -20)
.attr('y', (d, i) => i * 15 + i * 1 + 9);
const monthFirstDays = data.filter(e => e.date.substring(8, 10) === '01');
// 抓出每個月第一天的data
g
.selectAll('.month-label')
.data(monthFirstDays)
.enter()
.append('text')
.attr('class', 'month-label')
.text((d) =>
moment(d.date)
.format('M')
)
.attr('font-size', 8)
.attr('fill', '#B8B8B8')
.attr('text-anchor', 'middle')
.attr('x', (d) => (d.weekNum - 1) * 15 + (d.weekNum - 1) * 1 + 7.5)
.attr('y', -5);